﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Media;
using System.IO;
namespace Pomodoro
{
    public partial class frmMain : ConfigForm
    {
        private enum SpanType
        {
            Atom,
            ShortBreak,
            LongBreak
        }

        private const bool USE_SECONDS = false; //this is a debugging flag that turns the time constants below into seconds instead of minutes (for easier testing)

        private Timer timer = new Timer();
        private Timer updater = new Timer();
        private Stopwatch stopWatch = new Stopwatch();

        private TimeSpan ONE_MINUTE = new TimeSpan(0, 1, 0);
        private TimeSpan FIVE_SECONDS = new TimeSpan(0, 0, 5);

        private Dictionary<SpanType, TimeSpan> _spans;
        private Dictionary<SpanType, TimeSpan> Spans
        {
            get 
            {
                if (_spans == null)
                {
                    _spans = new Dictionary<SpanType, TimeSpan>();
                    
                    int second_multiplier = USE_SECONDS ? 1 : 60;

                    _spans.Add(SpanType.Atom, new TimeSpan(0, 0, AtomTime * second_multiplier));
                    _spans.Add(SpanType.ShortBreak, new TimeSpan(0, 0, ShortBreakTime * second_multiplier));
                    _spans.Add(SpanType.LongBreak, new TimeSpan(0, 0, LongBreakTime * second_multiplier));
                }
                return _spans; 
            }
        }

        private SpanType _currentSpanType;
        private SpanType CurrentSpanType
        {
            get { return _currentSpanType; }
            set { _currentSpanType = value; }
        }

        public TimeSpan CurrentTimeSpan
        {
            get
            {
                return Spans[CurrentSpanType];
            }
        }

        private int atomCount = 0;

        public frmMain()
        {
            LoadConfig();
            InitializeComponent();

            updater.Interval = 250;
            updater.Tick += new EventHandler(updater_Tick);
            CurrentSpanType = SpanType.Atom;
            timer.Tick += new EventHandler(timer_Tick);

            ResetClockDisplay();
            SetLongBreakLabel();
        }

     

        void timer_Tick(object sender, EventArgs e)
        {
            TimerFinished();
        }

        private void updater_Tick(object sender, EventArgs e)
        {
            SetCurrentTimeLabel();
            SetLongBreakLabel();
            PlaySound(GetTimeRemaining());
        }

       

        private void SetLongBreakLabel()
        {
            lblLongBreak.Text =     
                String.Format("Approximate time until next long break: {0}"
                , FormatTimeHours(TimeUntilNextLongBreak()));
        }

        private string FormatTimeHours(TimeSpan timeSpan)
        {
            return FormatTime(timeSpan, true);
        }

        private TimeSpan TimeUntilNextLongBreak()
        {
            if (CurrentSpanType == SpanType.LongBreak)
                return TimeSpan.Zero;

            return CalculateTimeRemaining(AtomsBeforeLongBreak - atomCount);
        }

        private TimeSpan CalculateTimeRemaining(int atomsRemaining)
        {
            var timeRemaining =
                new TimeSpan(Spans[SpanType.Atom].Ticks * atomsRemaining)
                + new TimeSpan(Spans[SpanType.ShortBreak].Ticks * (atomsRemaining - 1));

            if (CurrentSpanType == SpanType.ShortBreak)
            {
                timeRemaining += Spans[SpanType.ShortBreak];
            }

            timeRemaining -= stopWatch.Elapsed;

            Debug.Assert(timeRemaining > TimeSpan.Zero);

            return timeRemaining;
        }

        private void SetCurrentTimeLabel()
        {
            lblCurrentTime.Text = FormatTime(GetTimeRemaining());
            lblCurrentTime.ForeColor = SetClockColor(GetTimeRemaining());
        }

        private void PlaySound(TimeSpan timeSpan)
        {
            if (timeSpan < FIVE_SECONDS)
            {
                SystemSounds.Beep.Play();
            }
        }

        private Color SetClockColor(TimeSpan timeSpan)
        {
            return (timeSpan > ONE_MINUTE) ? Color.Black : Color.Red;
        }

        private string FormatTime(TimeSpan timeSpan)
        {
            return FormatTime(timeSpan, false);
        }

        private string FormatTime(TimeSpan timeSpan, bool includeHours)
        {
            if (includeHours)
            {
                return String.Format("{0:00}:{1:00}:{2:00}", timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
            }

            return String.Format("{0:00}:{1:00}", timeSpan.Minutes, timeSpan.Seconds);
        }

        private TimeSpan GetTimeRemaining()
        {
            return CurrentTimeSpan.Subtract(stopWatch.Elapsed);
        }

        private void StartTimer()
        {
            timer.Interval = Convert.ToInt32(CurrentTimeSpan.TotalMilliseconds);
            timer.Start();
            stopWatch.Start();
            updater.Start();
        }

        private void StopTimer()
        {
            timer.Stop();
            stopWatch.Stop();
            stopWatch.Reset();
            updater.Stop();
            ResetClockDisplay();
            SetLongBreakLabel();
            
            SystemSounds.Exclamation.Play();

            if (CurrentSpanType != SpanType.Atom)
                new frmInterrupt().Show();
        }

        private void ResetClockDisplay()
        {
            lblCurrentTime.Text = FormatTime(CurrentTimeSpan);
        }

        private void TimerInterupted()
        {
            StopTimer();
        }

        private void TimerFinished()
        {
            SetNewTimeSpan();
            StopTimer();
            
            if (CurrentSpanType != SpanType.Atom)
            {
                StartTimer();
            }

            btnStop.Enabled = false;
            btnStart.Enabled = true;
        }

        private void SetNewTimeSpan()
        {
            if (CurrentSpanType == SpanType.Atom)
            {
                if (atomCount < AtomsBeforeLongBreak - 1)
                {
                    CurrentSpanType = SpanType.ShortBreak;
                    atomCount++;
                }
                else
                {
                    CurrentSpanType = SpanType.LongBreak;
                    atomCount = 0;
                }
            }
            else
            {
                CurrentSpanType = SpanType.Atom;
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (BreakIsInProgress())
            {
                SetNewTimeSpan();
                StopTimer();
            }
            
            StartTimer();
            btnStart.Enabled = false;
            btnStop.Enabled = true;
        }

        private bool BreakIsInProgress()
        {
            return CurrentSpanType != SpanType.Atom && stopWatch.IsRunning;
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            TimerInterupted();
            btnStop.Enabled = false;
            btnStart.Enabled = true;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void configurationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (new frmConfig().ShowDialog() == DialogResult.OK)
            {
                _spans = null;
                LoadConfig();
                ResetClockDisplay();
                SetLongBreakLabel();
            }
        }
    }
}
